use crate::token::{Token, TokenType, lookup_ident};

#[derive( Clone, Debug)]
pub struct Lexer{
    input: String,
    position: usize,
    read_position: usize,
    ch: char,
}

// pub fn init_lexer<'a>(input: &'a str) -> Lexer{
//     let mut lexer = Lexer{
//         input: input.to_string(),
//         position: 0,
//         read_position: 0,
//         ch: '\0' , // 使用 0 或 b'\0' 表示空字节
//     };
//     lexer.read_char();
//     lexer
// }

impl Lexer {
    pub fn new(input: String) -> Self{
        let mut lexer = Lexer{
        input: input.to_string(),
        position: 0,
        read_position: 0,
        ch: '\0' , // 使用 0 或 b'\0' 表示空字节
    };
    lexer.read_char();
    lexer
    }
    fn read_char(&mut self){
        if self.read_position >= self.input.len(){
            self.ch = '\0';  
        }else{
            if let Some(c) = self.input.chars().nth(self.read_position){
                self.ch = c;
            }
        }
        self.position = self.read_position;
        self.read_position += 1;
    }

    //只窥看下一个字符，并不移动readPosition
    fn peek_char(&self) -> char{
        if self.read_position >= self.input.len(){
            '\0'  
        }else{
            match self.input.chars().nth(self.read_position){
                Some(c) => c,
                _ => '\0',
            }
        }
    }

    //读取空格和换行
    fn skip_whitespace(&mut self){
        while self.ch == ' ' || self.ch == '\t' || self.ch == '\n' || self.ch == '\r' {
            self.read_char();
        }
    }

    fn read_identifier<'a>(&'a mut self) -> &'a str{
        let position = self.position;
        while is_letter(self.ch){
            self.read_char();
        }
        &self.input[position..self.position]
    }

    fn read_number<'a>(&'a mut self) -> &'a str{
        let position = self.position;
        while is_digit(self.ch){
            self.read_char();
        }
        &self.input[position..self.position]
    }

    pub fn next_token(&mut self) -> Token{
        let token: Token;
        self.skip_whitespace();
        // println!("self.ch:{}", self.ch);
        match self.ch {
            '+' => {
                token = Token::new(TokenType::PLUS, self.ch);
            },
            '-' => {
                token = Token::new(TokenType::MINUS, self.ch);
            },
            '*' => {
                token = Token::new(TokenType::ASTERISK, self.ch);
            },
            '/' => {
                token = Token::new(TokenType::SLASH, self.ch);
            },
            '<' => {
                token = Token::new(TokenType::LT, self.ch);
            },
            '>' => {
                token = Token::new(TokenType::GT, self.ch);
            },
            '=' => {
                if self.peek_char() == '=' {
                    let ch = self.ch;
                    self.read_char();
                    let mut literal = String::new();
                    literal.push(ch);
                    literal.push(self.ch);
                    token = Token{
                        token_type: TokenType::EQ,
                        literal: literal,
                    };
                    // token.literal = literal;
                }else{
                    token = Token::new(TokenType::ASSIGN, self.ch);
                }
                
            },
            '!' => {
                if self.peek_char() == '=' {
                    let ch: char = self.ch;
                    self.read_char();
                    let mut literal = String::new();
                    literal.push(ch);
                    literal.push(self.ch);
                    token = Token{
                        token_type: TokenType::NOT_EQ,
                        literal: literal,
                    };
                    
                }else{
                    token = Token::new(TokenType::BANG, self.ch);
                }
                
            },
            ',' => {
                token = Token::new(TokenType::COMMA, self.ch);
                
            },
            ';' => {
                token = Token::new(TokenType::SEMICOLON, self.ch);
                
            },
            '(' => {
                token = Token::new(TokenType::LPAREN, self.ch);
                
            },
            ')' => {
                token = Token::new(TokenType::RPAREN, self.ch);
                
            },
            '{' => {
                token = Token::new(TokenType::LBRACE, self.ch);
                
            },
            '}' => {
                token = Token::new(TokenType::RBRACE, self.ch);
                
            },
            '\0' => {
                token = Token{
                    token_type: TokenType::EOF,
                    literal: String::new(),
                };
            },
            _ => {
                if is_letter(self.ch){
                    let literal = self.read_identifier();
                    return Token{
                        token_type: lookup_ident(literal),
                        literal: literal.to_string(),
                    }
                }else if is_digit(self.ch){
                    return Token{
                            token_type: TokenType::INT,
                            literal: self.read_number().to_string(),
                        }
                }else{
                    token = Token::new(TokenType::ILLEGAL, self.ch);
                    // self.read_char();
                    // token
                }
            }
        }
        self.read_char();
        token
    }

    
}

// pub fn Token::new(token_type: TokenType, ch: char) -> Token{
//     Token{
//         token_type: token_type,
//         literal: ch.to_string(),
//     }
// }

fn is_letter(ch: char) -> bool{
    ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_'
}

fn is_digit(ch: char) -> bool{
    ch >= '0' && ch <= '9'
}
